我們先把什麼Vue不Vue的,React不React的放一旁,從一個很基礎,但很多初學者(包含我)卻沒有好好去理解的關鍵概念開始認識,那就是mutable和immutable。如果在網路上查詢這兩個關鍵字的話,通常都可以先接受到一個結論,也就是「普通型別的特性是immutable,物件型別是的特性則是mutable
」。但是究竟是什麼意思呢?就接著給他看下去!
從字面上看的話,就是「可變(mutable)」和「不可變(immutable)」的意思。那變不變的部分指的是什麼呢?這裡變不變指的是「存在特定記憶體位址的值可變和不可變
」。這樣說可能還是很模糊,先讓我們回歸到最單純的JavaScript。
在宣告一個變數時,通常都會幫變數保留一個記憶體空間,不論是原始型別或是物件型別都一樣會有一個記憶體空間,但是原始型別和物件型別卻有一個不一樣的特性,那就是大家應該都知道或聽過的原始型別是傳值(by value),物件型別是傳址(by reference),而這樣的特性也就跟可變和不可變有關聯。
如果是一般型別的話,在「宣告變數」、「重新賦值」、「以這個既有變數來宣告新變數」、「在用既有變數來宣告新的變數後,改動新變數的值」的這四種常見的操作,主要會是以下這幾個狀況。
- 宣告變數
JavaScript會幫這個變數建立一個記憶體空間,並且把變數名稱指向這個記憶體空間。(補充: 用var宣告變數,一開始會先給變數一個undefined,最後才會指向實際要賦予的值)
var number = 1;
- 重新賦值
JavaScript會重新幫新的值建立另一個空間,並把這個變數名稱指向新值的記憶體空間,如果沒有其他變數指向原本這個值的記憶體空間,這個記憶體空間就會透過JavaScript的Garbage collection機制把這個記憶體給釋放出來。
var number = 1;
number = 2;
- 以這個既有變數來宣告新的變數
如果把用原本就有的變數賦值給新的變數時,則會變成JavaScript會把這個變數原本的值給複製一份出來,並且保留一個新的記憶體空間給被複製出來的值,再讓新的變數指向這個新的記憶體空間,也就是說雖然既有的變數和新的變數以肉眼來看值都是一樣的,但其實卻是不同的記憶體空間。
var number = 1;
var newNumber = number;
- 在用既有變數來宣告新的變數後,改動新變數的值
在這個情境下,如前面所提到的既有變數和新的變數雖然肉眼看都是相同的值,但是值所存在的記憶體空間卻不相同,所以當改動新變數時,既有的變數並不會變改動到。
var number = 1;
var newNumber = number;
newNumber = 4;
以上這些情境也就說明到了傳值(by value),以及不可變動(immutable)的特性。「如果把用原本就有的變數賦值給新的變數時,則會變成JavaScript會把這個變數原本的值給複製一份出來,並且保留一個新的記憶體空間給被複製出來的值,再讓新的變數指向這個新的記憶體空間」這句話也就是在說明傳值
;「既有變數和新的變數雖然肉眼看都是相同的值,但是值所存在的記憶體空間卻不相同,所以當改動新變數時,既有的變數並不會變改動到」這部分也就是不可變動
,因為原本的值永遠都不會有變動。
接下來看一下傳址(by reference)和可變動(mutable)的部分,這兩個特性會出現在物件型別。我們一樣用「宣告變數」、「重新賦值」、「以這個既有變數來宣告新變數」、「在用既有變數來宣告新的變數後,改動新變數中其中一屬性的值」的這四種常見的變數操作來看會是什麼樣的狀況。
- 宣告變數
JavaScript會幫這個變數建立一個記憶體空間,並且把變數名稱指向這個記憶體空間。這部分跟原始型別沒有差異。
var profile = {
name: 'Phoebe',
age: 18,
};
- 重新賦值
JavaScript會幫新的值保留一個新的記憶體空間,讓這個變數指向新的記憶體空間。
var profile = {
name: 'Phoebe',
age: 18,
};
profile = {
name: 'Jolin',
age: 18,
}
- 以這個既有變數來宣告新的變數
如果想把用原本就有的變數賦值給新的變數時,新的變數會去指向既有變數值的記憶體空間,也就是不僅肉眼看起來是一樣的值,連記憶體空間都相同。
var profile = {
name: 'Phoebe',
age: 18,
};
var otherProfile = profile;
- 在用既有變數來宣告新的變數後,改新變數中其中一屬性的值
在這個情境下,如前面所提到的既有變數和新的變數不僅肉眼看起來一樣,記憶體也一樣,所以當改動新變數時,既有的變數當然也會被變動到,因為這兩個變數雖然變數名不同,但其實值都是同一組。
var profile = {
name: 'Phoebe',
age: 18,
};
var otherProfile = profile;
otherProfile.name = 'Jolin';
說到這裡其實應該可以感受到跟前面原始型別的差異了。沒錯!以上這些內容就包含了傳址(by reference),以及可變動(mutable)的特性。「如果想把用原本就有的變數賦值給新的變數時,新的變數會去指向既有變數值的記憶體空間,也就是不僅肉眼看起來是一樣的值,連記憶體空間都相同」這句話也就是在說明傳址
,因為他不單單是給新變數相同的值,連記憶體位址都傳給新的變數;「既有變數和新的變數不僅肉眼看起來一樣,記憶體也一樣,所以當改動新變數時,既有的變數當然也會被變動到」這部分也就是可變動
的特性,因為原本的值是會被改動到的,在改動相關的操作時,並不會給一個新記憶體空間的值,而是改動到既有記憶體空間的值。
那就是「vue主要是使用mutable的特性,React則相反是主要使用immutale的特性」
。雖然不仔細思考,感覺vue和react的用法似乎大同小異,尤其vue3開始,寫法又跟react更相似了,都是用所謂的hooks在做一些操作。但仔細研究的話,其實就可以發現從大至渲染的機制,小至state的改動操作,Vue跟React在根本上都各自帶著mutable和immutable的特性,也因為如此,了解這兩個特性也就變得很重要!
明天就讓我們從最基底的渲染機制,來延伸感受一下mutable和immutable特性為Vue和React所帶來的差異是什麼。